/*-------------------------------------------------------------------------
 *
 * hash_xlog.h
 *	  Postgres hash AM 实现的头文件
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/access/hash_xlog.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef HASH_XLOG_H
#define HASH_XLOG_H

#include "access/xlogreader.h"
#include "lib/stringinfo.h"
#include "storage/off.h"

/* XLOG_HASH_SQUEEZE_PAGE 操作所需的缓冲区数量 */
#define HASH_XLOG_FREE_OVFL_BUFS	6

/*
 * 哈希操作的 XLOG 记录
 */
#define XLOG_HASH_INIT_META_PAGE	0x00	/* 初始化元数据页面 */
#define XLOG_HASH_INIT_BITMAP_PAGE	0x10	/* 初始化位图页面 */
#define XLOG_HASH_INSERT		0x20	/* 添加索引元组而不进行拆分 */
#define XLOG_HASH_ADD_OVFL_PAGE 0x30	/* 添加溢出页面 */
#define XLOG_HASH_SPLIT_ALLOCATE_PAGE	0x40	/* 为拆分分配新页面 */
#define XLOG_HASH_SPLIT_PAGE	0x50	/* 拆分页面 */
#define XLOG_HASH_SPLIT_COMPLETE	0x60	/* 拆分操作的完成 */
#define XLOG_HASH_MOVE_PAGE_CONTENTS	0x70	/* 从一个页面中移除元组
												 * 并添加到另一个页面 */
#define XLOG_HASH_SQUEEZE_PAGE	0x80	/* 将元组添加到链中之前的
										 * 页面之一，并释放溢出
										 * 页面 */
#define XLOG_HASH_DELETE		0x90	/* 从页面删除索引元组 */
#define XLOG_HASH_SPLIT_CLEANUP 0xA0	/* 在删除因拆分而移动的元组后
										 * 清除主
										 * 桶页面中的拆分清理标志 */
#define XLOG_HASH_UPDATE_META_PAGE	0xB0	/* 在清理后更新元数据页面 */

#define XLOG_HASH_VACUUM_ONE_PAGE	0xC0	/* 从索引
											 * 页面中移除死元组 */

/*
 * xl_hash_split_allocate_page 标志值，提供 8 位。
 */
#define XLH_SPLIT_META_UPDATE_MASKS		(1<<0)
#define XLH_SPLIT_META_UPDATE_SPLITPOINT		(1<<1)

/*
 * 这是我们需要了解简单（无拆分）插入的内容。
 *
 * 此数据记录用于 XLOG_HASH_INSERT
 *
 * 后备块 0：原始页面（数据包含插入的元组）
 * 后备块 1：元数据页面（HashMetaPageData）
 */
typedef struct xl_hash_insert
{
	OffsetNumber offnum;
} xl_hash_insert;

#define SizeOfHashInsert	(offsetof(xl_hash_insert, offnum) + sizeof(OffsetNumber))

/*
 * 这是我们需要了解添加溢出页面的内容。
 *
 * 此数据记录用于 XLOG_HASH_ADD_OVFL_PAGE
 *
 * 后备块 0：新分配的溢出页面
 * 后备块 1：桶链中新溢出页面之前的页面
 * 后备块 2：位图页面
 * 后备块 3：新位图页面
 * 后备块 4：元数据页面
 */
typedef struct xl_hash_add_ovfl_page
{
	uint16		bmsize;
	bool		bmpage_found;
} xl_hash_add_ovfl_page;

#define SizeOfHashAddOvflPage	\
	(offsetof(xl_hash_add_ovfl_page, bmpage_found) + sizeof(bool))

/*
 * 这是我们需要了解为拆分分配页面的内容。
 *
 * 此数据记录用于 XLOG_HASH_SPLIT_ALLOCATE_PAGE
 *
 * 后备块 0：旧桶的页面
 * 后备块 1：新桶的页面
 * 后备块 2：元数据页面
 */
typedef struct xl_hash_split_allocate_page
{
	uint32		new_bucket;
	uint16		old_bucket_flag;
	uint16		new_bucket_flag;
	uint8		flags;
} xl_hash_split_allocate_page;

#define SizeOfHashSplitAllocPage	\
	(offsetof(xl_hash_split_allocate_page, flags) + sizeof(uint8))

/*
 * 这是我们需要了解完成拆分操作的内容。
 *
 * 此数据记录用于 XLOG_HASH_SPLIT_COMPLETE
 *
 * 后备块 0：旧桶的页面
 * 后备块 1：新桶的页面
 */
typedef struct xl_hash_split_complete
{
	uint16		old_bucket_flag;
	uint16		new_bucket_flag;
} xl_hash_split_complete;

#define SizeOfHashSplitComplete \
	(offsetof(xl_hash_split_complete, new_bucket_flag) + sizeof(uint16))

/*
 * 这是我们在挤压操作期间需要了解的有关移动页面内容的信息。
 *
 * 此数据记录用于 XLOG_HASH_MOVE_PAGE_CONTENTS
 *
 * 备份块 0：桶页面
 * 备份块 1：包含移动元组的页面
 * 备份块 2：将从中删除元组的页面
 */
typedef struct xl_hash_move_page_contents
{
	uint16		ntups;
	bool		is_prim_bucket_same_wrt;	/* 如果移动的元组所在的页面与
											 * 主桶页面相同则为真 */
} xl_hash_move_page_contents;

#define SizeOfHashMovePageContents	\
	(offsetof(xl_hash_move_page_contents, is_prim_bucket_same_wrt) + sizeof(bool))

/*
 * 这是我们需要了解的关于挤压页面操作的信息。
 *
 * 此数据记录用于 XLOG_HASH_SQUEEZE_PAGE
 *
 * 备份块 0：包含从已释放溢出页面移动的元组的页面
 * 备份块 1：已释放的溢出页面
 * 备份块 2：已释放的溢出页面之前的页面
 * 备份块 3：已释放的溢出页面之后的页面
 * 备份块 4：包含已释放溢出页面信息的位图页面
 * 备份块 5：元数据页面
 */
typedef struct xl_hash_squeeze_page
{
	BlockNumber prevblkno;
	BlockNumber nextblkno;
	uint16		ntups;
	bool		is_prim_bucket_same_wrt;	/* 如果移动的元组所在的页面与
											 * 主桶页面相同则为真 */
	bool		is_prev_bucket_same_wrt;	/* 如果移动的元组所在的页面
											 * 是已释放溢出页面之前的页面则为真 */
} xl_hash_squeeze_page;

#define SizeOfHashSqueezePage	\
	(offsetof(xl_hash_squeeze_page, is_prev_bucket_same_wrt) + sizeof(bool))

/*
 * 这是我们需要了解的关于从页面中删除索引元组的信息。
 *
 * 此数据记录用于 XLOG_HASH_DELETE
 *
 * 备份块 0：主桶页面
 * 备份块 1：从中删除元组的页面
 */
typedef struct xl_hash_delete
{
	bool		clear_dead_marking; /* 如果此操作清除了
									 * LH_PAGE_HAS_DEAD_TUPLES 标志则为真 */
	bool		is_primary_bucket_page; /* 如果此操作针对
										 * 主桶页面则为真 */
} xl_hash_delete;

#define SizeOfHashDelete	(offsetof(xl_hash_delete, is_primary_bucket_page) + sizeof(bool))

/*
 * 这是我们需要用于元数据页面更新操作的信息。
 *
 * 此数据记录用于 XLOG_HASH_UPDATE_META_PAGE
 *
 * 备份块 0：元数据页面
 */
typedef struct xl_hash_update_meta_page
{
	double		ntuples;
} xl_hash_update_meta_page;

#define SizeOfHashUpdateMetaPage	\
	(offsetof(xl_hash_update_meta_page, ntuples) + sizeof(double))

/*
 * 这是我们需要初始化元数据页面的信息。
 *
 * 此数据记录用于 XLOG_HASH_INIT_META_PAGE
 *
 * 备份块 0：元数据页面
 */
typedef struct xl_hash_init_meta_page
{
	double		num_tuples;
	RegProcedure procid;
	uint16		ffactor;
} xl_hash_init_meta_page;

#define SizeOfHashInitMetaPage		\
	(offsetof(xl_hash_init_meta_page, ffactor) + sizeof(uint16))

/*
 * 这是我们需要初始化位图页面的信息。
 *
 * 此数据记录用于 XLOG_HASH_INIT_BITMAP_PAGE
 *
 * 备份块 0：位图页面
 * 备份块 1：元数据页面
 */
typedef struct xl_hash_init_bitmap_page
{
	uint16		bmsize;
} xl_hash_init_bitmap_page;

#define SizeOfHashInitBitmapPage	\
	(offsetof(xl_hash_init_bitmap_page, bmsize) + sizeof(uint16))

/*
 * 这是我们需要为了索引元组删除和更新元数据页面的信息。
 *
 * 此数据记录用于 XLOG_HASH_VACUUM_ONE_PAGE
 *
 * 备份块 0：桶页面
 * 备份块 1：元数据页面
 */
typedef struct xl_hash_vacuum_one_page
{
	TransactionId latestRemovedXid;
	int			ntuples;

	/* 目标偏移数量在最后跟随 */
} xl_hash_vacuum_one_page;

#define SizeOfHashVacuumOnePage \
	(offsetof(xl_hash_vacuum_one_page, ntuples) + sizeof(int))

extern void hash_redo(XLogReaderState *record);
extern void hash_desc(StringInfo buf, XLogReaderState *record);
extern const char *hash_identify(uint8 info);
extern void hash_mask(char *pagedata, BlockNumber blkno);

#endif							/* HASH_XLOG_H */
